﻿#define DynamicBrushes

using System;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Globalization;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Input;

namespace rgr
{
    /// <summary>A Node for representation in a treemap
    /// </summary>
    /// <remarks>
    /// An RGRTreeMapNode represents on node in a tree that is ulimately
    /// rendered as a tree map.  This node structure is independant
    /// of the layout and rendering algorithm.
    /// 
    /// Each node in the tree has a size property.  This represents the
    /// logical size of a node and is used to compute the area of ech
    /// visible element in the tree map. 
    /// </remarks>
    public abstract class RGRTreeMapNode : IComparable<RGRTreeMapNode>
    {
        /// <summary>Construct a RGRTreeMapNode
        /// </summary>
        /// <param name="tName">the name of the node.  This may be null</param>
        public RGRTreeMapNode(String tName)
        {
            myName = tName;
        }

        /// <summary>Adds a set of RGRTreeMapNode objects to the target node as children.
        /// </summary>
        /// <param name="NewChildren">
        /// An array of RGRTreeMapNodel objects that will become
        /// the children of the target node.  Passing null as this
        /// parameter will remove all this node's children.
        /// </param>
        /// <remarks>
        /// If this node already has children then they are oprhaned
        /// and their parent property is set to to null.  This
        /// function sorts the array according to the size of 
        /// each child node.  The sort is decending with largest
        /// size node at myChildren[0] and the smallest at 
        /// myChildren[n-1].
        /// </remarks>
        public void AddChildren(RGRTreeMapNode[] NewChildren)
        {
            if (myChildren != null)
            {
                for (int i = 0; i < myChildren.Length; ++i)
                {
                    myChildren[i].myParent = null;
                }
            }

            if (NewChildren == null)
            {
                myChildren = null;
                myTotalChildSize = 0.0;
                return;
            }

            myChildren = NewChildren;
            myTotalChildSize = 0.0;

            for (int i = 0; i < myChildren.Length; ++i)
            {
                myChildren[i].myParent = this;
                myTotalChildSize += myChildren[i].Size;
            }

            for (int i = 0; i < myChildren.Length; ++i)
            {
                myChildren[i].mySizeRatio = myChildren[i].Size / myTotalChildSize;
            }

            Array.Sort(myChildren);
        }

        /// <summary>Compares two RGRTreeMapNode objects by the value property
        /// </summary>
        /// <param name="left"></param>
        /// <param name="ritht"></param>
        /// <returns>a value less than zero if other.Value &lt this.Value
        ///          zero if the values are equal
        ///          a value greater than zero if other.Value &gt this.Value
        /// </returns>
        /// <remarks>
        /// This function guarantees that lists will be sorted from largst size
        /// to smallst size.   Where the largest item is at the front (lowest
        /// index) postion, and the smallest is at the end.
        /// </remarks>
        public int CompareTo(RGRTreeMapNode other)
        {
            const double PosEpsilonDoubleX2 = double.Epsilon;
            const double NegEpsilonDoubleX2 = -double.Epsilon;

            double v = other.Size - this.Size;

            if (v > PosEpsilonDoubleX2)
            {
                return 1;
            }
            else
                if (v < NegEpsilonDoubleX2)
                {
                    return -1;
                }
                else
                {
                    return 0;
                }
        }

        /// <summary>Compute the maximum number of children in of any node.
        /// </summary>
        /// <returns>
        /// The maximum number of children in of any node.  This may
        /// be zero.
        /// </returns>
        /// <remarks>
        /// The value used by this function is used to pre-allocate
        /// lists that are used to render the tree.  E.g. the 
        /// lists are big enough to handle any child manipulion tasks
        /// that are needed by the tree mapy layout and rendering logic.
        /// This avoids memory allcoations during the rendering process.
        /// 
        /// Note this fnction traveres the tree recurisivly so it 
        /// may be computationly expensive for large trees.
        /// </remarks>
        public int ComputeMaxChildCount()
        {
            return ComputeMaxChildrenRecursively(0);
        }

        /// <summary>This is the recurisve worker function for ComputeMaxChildCont();
        /// </summary>
        private int ComputeMaxChildrenRecursively(int CurrentMax)
        {
            if (myChildren == null || myChildren.Length == 0)
            {
                return 0;
            }

            int maxcc = myChildren.Length;

            for (int i = 0; i < myChildren.Length; ++i)
            {
                maxcc = Math.Max(maxcc, myChildren[i].ComputeMaxChildrenRecursively(maxcc));
            }

            return maxcc;
        }

        /// <summary>Finds a node by point
        /// </summary>
        /// <param name="p">A point that is relative to the tree map control.</param>
        /// <returns>
        /// A reference to the lowest node in the tre that contains
        /// the point. Null is returned if there is no node in the tree that contains
        /// the point.
        /// 
        /// Note that this function operations on the tree recursivly so may be 
        /// computatinly expensive for large trees.
        /// </returns>
        public RGRTreeMapNode FindNodeByPoint(Point p)
        {
            int ccnt = 0;
            RGRTreeMapNode n = FindNodeByPointR(p, ref ccnt);
            if (n != null)
            {
                n._CompareCount = ccnt;
            }
            return n;
        }

        public bool RemainderBoundingRectCointains(Point p)
        {
            return RemainderBoundingRect.Contains(p);
        }

        private RGRTreeMapNode FindNodeByPointR(Point p, ref int ccnt)
        {
            ++ccnt;
            if (!BoundingRect.Contains(p))
            {
                return null;
            }

            if (!RemainderBoundingRect.IsEmpty)
            {
                ++ccnt;
                if (RemainderBoundingRect.Contains(p))
                {
                    return this;
                }
            }

            if (myChildren != null)
            {
                for (int i = 0; i < myChildren.Length; ++i)
                {
                    ++ccnt;
                    if (myChildren[i].BoundingRect.Contains(p))
                    {
                        return myChildren[i].FindNodeByPointR(p, ref ccnt);
                    }
                }
            }
            return this;
        }

        /// <summary>Computes the depth of a node.
        /// </summary>
        /// <returns>The node's depth.  0 means the node is the node
        /// of the tree.  1 means the node is a child of the node.  2 means
        /// that the node is one level away from the node and so on.
        /// 
        /// Note that this function might be computationly intensive for
        /// very deep trees.
        /// </returns>
        public int ComputeDepth()
        {
            int d = 0;

            RGRTreeMapNode p = this;

            while ((p = p.Parent) != null)
            {
                ++d;
            }

            return d;
        }

        // --- properties ---

        /// <summary>
        /// The name of the node.  This may be null
        /// </summary>
        public virtual String Name
        {
            get { return myName; }
            set { myName = value; }
        }
        private String myName;

        /// <summary>A reference to the node's parent. 
        /// This will be null if the node is not part of a tree or if it is 
        /// the node node of a tree.
        /// </summary>
        public RGRTreeMapNode Parent { get { return myParent; } }
        private RGRTreeMapNode myParent;

        /// <summary>The list of a node's children.
        /// Note that this may be null
        /// if the node has no children. 
        /// </summary>
        public RGRTreeMapNode[] Children { get { return myChildren; } }
        public RGRTreeMapNode[] myChildren;

        /// <summary>The size of a node.   
        /// </summary>
        /// <remarks>
        /// This value is unitless as far as the tree map conrol is concerned.
        /// It is used to compute the area of the visible object that represents
        /// a particular node.   
        /// </remarks>
        public abstract double Size { get; }

        /// <summary>The ratio of this node's Size to the total size of of all this node's siblings. 
        /// </summary>
        public double SizeRatio { get { return mySizeRatio; } }
        double mySizeRatio;


        /// <summary>This is the sum of the Size properies of all this node's children.
        /// </summary>
        public int ChildCount { get { return myChildren == null ? 0 : myChildren.Length; } }

        /// <summary>This is the sum of the Size properies of all this node's children.
        /// </summary>
        public double TotalChildSize { get { return myTotalChildSize; } }
        private double myTotalChildSize;

        /// <summary>The visible bounding rectangle for this node.
        /// </summary>
        internal Rect BoundingRect;

        /// <summary>The visible rectangle of a node that has no children.
        /// </summary>
        /// <remarks>
        /// Not all children may be large enough to be rendered.  In this
        /// case the remaning space for a node is computed as this
        /// remainder bounding rectangle.
        /// </remarks>
        internal Rect RemainderBoundingRect = Rect.Empty;

        /// <summary>The area of the visible bounding rectnagle for this node.
        /// </summary>
        internal double Area;

        /// <summary>The actual number of children to render.
        /// </summary>
        /// <remarks>
        /// This value will be less than or equal to myChildren.Length.
        /// This property is used to limit the rendering of nodes
        /// that render too small to be effecivly visible.
        /// </remarks>
        internal int NumToRender;

        /// <summary>
        /// The brush used to paint this particular node
        /// </summary>
        internal Brush AreaBrush;

        /// <summary>
        /// The brush used to paint the remainder retangle.
        /// </summary>
        internal Brush RemainderBrush;

        /// <summary>
        /// The formatted text to draw for the node
        /// </summary>
        internal FormattedText FText;

        /// <summary>
        /// The location of the formatted text.
        /// </summary>
        internal Point FTextLocation;

        /// <summary>
        /// The opacity for the text
        /// </summary>
        internal Double FTextOpacity;

        /// <summary>
        /// The transform used to render the formatted text
        /// </summary>
        internal ScaleTransform FTextScaleTransform;

        /// <summary>
        /// The number of compares need to find a node by a point
        /// </summary>
        public int CompareCount { get { return _CompareCount; } }
        private int _CompareCount;

        /// <summary>
        /// The number of ticks it took to hit test the last mouse over for this node
        /// </summary>
        public long MouseOverTicks { get { return _CompareCount; } }
        internal long _MouseOverTicks;

       
    }

    //==============================================================================================================================

    public struct RenderPerfRecord
    {
        public RenderPerfRecord(long rt, long pc, long rc)
        {
            RunTime = rt;
            PaintedRects = pc;
            RenderCounts = rc;
        }
        long RunTime;
        long PaintedRects;
        long RenderCounts;
    }

    /// <summary>A TreeMap control
    /// </summary>
    public partial class RGRTreeMapClass
    {
        /// <summary>Constructor
        /// </summary>
        public RGRTreeMapClass()
        {
            this.InitializeComponent();

            TextTypeFace = new Typeface("Veranda");
            TextSize = 10;
            TextBrush = Brushes.Black;
        }

        /// <summary>
        /// Handlerender size changes by recomputing all the bounding rectangls, text and adornmens
        /// </summary>
        protected override void OnRenderSizeChanged(SizeChangedInfo info)
        {
            // get the tree of elements that will be rendered
            RGRTreeMapNode root = (RGRTreeMapNode)GetValue(TreeMapDataProperty);

            if (root == null)
            {
                // this is important: if there is no tree, then we render nothing
                // the tree map will be transparent.
                return;
            }

            RGRTreeMapNode zr = ZoomRoot;

            ComputeAllNodeBoundingRects(zr == null ? root : zr, info.NewSize.Width, info.NewSize.Height);
        }

        /// <summary>
        /// Compute all the bounding rectanngs, text sizes and adnorments for the tree
        /// </summary>
        /// <param name="node">computations begin at this node</param>
        private void ComputeAllNodeBoundingRects(RGRTreeMapNode root)
        {
            ComputeAllNodeBoundingRects(root, this.ActualWidth, this.ActualHeight);
        }

        /// <summary>
        /// Compute all the bounding rectanngs, text sizes and adnorments for the tree
        /// </summary>
        /// <param name="node">computations begin at this node</param>
        /// <param name="width">the width of the rectangele for the node node</param>
        /// <param name="height">the height of the rectangle for the node node</param>
        private void ComputeAllNodeBoundingRects(RGRTreeMapNode root, double width, double height)
        {
            ++ComputeBoundingRectsCount;

            long startTick = Stopwatch.GetTimestamp();

            //
            // the bounding rectangle for the node of the tree map is the 
            // rectangle for this control.
            //
            root.BoundingRect.X = 0;
            root.BoundingRect.Y = 0;
            root.BoundingRect.Width = width;
            root.BoundingRect.Height = height;
#if DynamicBrushes
            //root.AreaBrush = GetNextBrush();
#endif

            if (root.ChildCount == 0)
            {
                // the node has no children - just display the node
                ComputeNodeTextAndAdornments(root, 1);
                root.RemainderBoundingRect = Rect.Empty;
#if DynamicBrushes
                root.RemainderBrush = null;
#endif

            }
            else
            {
                // Get the area limit - rectangles less than this in size
                // will not be rendered
                TheAreaLimit = (double)GetValue(AreaLimitProperty);

                // Get the inset length 
                TheInsetLength = -1 * (double)GetValue(InsetLengthProperty);

                // Clear these performance counter.
                VisibleNodeCount = 0;
                VisibleRectCount = 0;
                VisibleRemainderRectCount = 0;

                ComputeSquareifiedTreeMap(root, 0);
            }

            if (_SendRenderPerfEvent && ComputeAllNodeBoundingRectsPerformanceSignal != null)
            {
                this.Parent.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Input,
                                                   ComputeAllNodeBoundingRectsPerformanceSignal,
                                                   Stopwatch.GetTimestamp() - startTick);
            }
        }

        /// <summary>squareified tree map algorithm. 
        /// </summary>
        /// <param name="node">the node on which to operate</param>
        /// rectangle to be sub-divided.
        /// </param>
        /// <remarks>
        /// This function computes the location and size for each of a
        /// a nodes bounding rectangles. 
        /// 
        /// Precondition: the node's rectangle must have a valid location and size
        ///               and must be intended to be visbile.
        /// Precondition: the node's children must be sorted by size
        ///               (largest first, smallest last)
        /// </remarks>
        private void ComputeSquareifiedTreeMap(RGRTreeMapNode node, int depth)
        {
            //Assert.IsTrue( node.BoundingRect.Width  > 0.0 );
            //Assert.IsTrue( node.BoundingRect.Height > 0.0 );
            //Assert.IsTrue( node.BoundingRect.Width * node.BoundingRect.Height >= AreaLimit );

            ++VisibleNodeCount; // this node is visible;
            ++VisibleRectCount; // its rect is visible

            if (node.ChildCount == 0)
            {
                // This node has no children so there is nothing to layout.
                // In this case, the remainder rect needs to be empty.
                node.RemainderBoundingRect = Rect.Empty;
            }
            else
            {
                //
                // Ther are one or more children to layout.  First step
                // is to compute the rectangle in which to lay them out
                //
                Rect lbr = node.BoundingRect; // layout bounding rect

                if (TheInsetLength < 0.0)
                {

                    double t;
                    if (depth == 1)
                    {
                        t = TheInsetLength * 3;
                    }
                    else
                        if (depth == 2)
                        {
                            t = TheInsetLength * 2;
                        }
                        else
                        {
                            t = TheInsetLength;
                        }

                    lbr.Inflate(t, t);

                    if (lbr.Width < 0 || lbr.Height < 0.0 || (lbr.Width * lbr.Height) <= AreaLimit)
                    {
                        //
                        // Ok, the resulting layout bounding rect is too small for any children
                        // or to even be a remainder rectangle.  So, we'll just stop here.  
                        // Setting node.NumToRender to zero tells the rendering logic that
                        // none of this node's children should be rendered.
                        //
                        node.NumToRender = 0;
                        node.RemainderBoundingRect = Rect.Empty;

                        // this node is too small to have any text.
                        node.FText = null;
                        node.FTextScaleTransform = null;
                        return;
                    }

                }
                else
                {
                    //Assert.IsTrue( lbr.Width  > 0.0 );
                    //Assert.IsTrue( lbr.Height > 0.0 );
                }

                //
                // The first step is to compute thea area of each child.  This call
                // will set the node's NumToRender vaue.
                //
                node.NumToRender = ComputeChildAreas(node, lbr, node.myChildren.Length, node.TotalChildSize);

                if (node.NumToRender == 0)
                {
                    //
                    // All the children are too small to render effecivly
                    // In this case, the remainder rect needs to be empty.
                    //
                    node.RemainderBoundingRect = Rect.Empty;

                }
                else
                    if (node.NumToRender == 1)
                    {
                        //
                        // If there is just one child for the node then just set the child
                        // to the lbr.
                        // In this case, the remainder rect needs to be empty.
                        //
                        node.RemainderBoundingRect = Rect.Empty;
#if DynamicBrushes
                        node.RemainderBrush = null;
#endif
                        node.myChildren[0].BoundingRect = lbr;

                        // Recursivly layout the one child for this node that needs
                        // to be rendered.
                        ComputeSquareifiedTreeMap(node.myChildren[0], depth + 1);

                    }
                    else
                    {
                        // 
                        // Squareify all the children of this node into its rectangle.
                        // Note that this call sets the node's remainder rect as 
                        // appropriate.
                        //
                        SquareifyChildren(node, lbr);

                        // Recursifly layout all of this node's children that
                        // are visible.
                        for (int i = 0; i < node.NumToRender; ++i)
                        {
                            ComputeSquareifiedTreeMap(node.myChildren[i], depth + 1);
                        }

                        if (!node.RemainderBoundingRect.IsEmpty)
                        {
                            ++VisibleRectCount;           // account for the visible rectangles
                            ++VisibleRemainderRectCount;  // count the visible remainder rects

                        }
                    }
            }

            ComputeNodeTextAndAdornments(node, depth);
        }

        /// <summary>
        /// Compute the text and any adornments for this node
        /// </summary>
        /// <param name="node"></param>
        /// <param name="depth"></param>
        public virtual void ComputeNodeTextAndAdornments(RGRTreeMapNode node, int depth)
        {
            //
            // The node node, and any node deeper than 2 do not get
            // any text or adornments
            //
            if (depth == 0 || depth > 2)
            {
                node.FText = null;
                node.FTextScaleTransform = null;
                return;
            }

            if (node.FText == null)
            {
                node.FText = new FormattedText(
                    node.Name,
                    CultureInfo.CurrentCulture,
                    CultureInfo.CurrentCulture.TextInfo.IsRightToLeft ? FlowDirection.RightToLeft : FlowDirection.LeftToRight,
                    TextTypeFace,
                    TextSize,
                    TextBrush
                    );

                
            }

            if (depth == 1)
            {
                node.FTextOpacity = 0.7;
            }
            else
            {
                node.FTextOpacity = 0.25;
            }

            // Draw the text centered in the node's bounding rectangle
            node.FTextLocation.X = node.BoundingRect.X + (node.BoundingRect.Width - node.FText.Width) / 2;
            node.FTextLocation.Y = node.BoundingRect.Y + (node.BoundingRect.Height - node.FText.Height) / 2;

            // Create a new unfrozen transform
            if (node.FTextScaleTransform == null)
            {
                node.FTextScaleTransform = new ScaleTransform();
            }
            else
            {
                node.FTextScaleTransform = node.FTextScaleTransform.Clone();
            }

            // The transform should scale form the center of the node's bounding rectangle
            node.FTextScaleTransform.CenterX = node.BoundingRect.X + node.BoundingRect.Width / 2;
            node.FTextScaleTransform.CenterY = node.BoundingRect.Y + node.BoundingRect.Height / 2;

            //
            // Make the text fit inside the node's bounding rectangle.  Note, we'll keep the
            // text's aspect ratio the same, scaling to a best fit by X or by Y, but not both
            //

            node.FTextScaleTransform.ScaleX = node.BoundingRect.Width / node.FText.Width;
            node.FTextScaleTransform.ScaleY = node.BoundingRect.Height / node.FText.Height;


            if ((node.FText.Height * node.FTextScaleTransform.ScaleX) > node.BoundingRect.Height)
            {
                node.FTextScaleTransform.ScaleX = node.FTextScaleTransform.ScaleY;
            }
            else
            {
                node.FTextScaleTransform.ScaleY = node.FTextScaleTransform.ScaleX;
            }

            node.FTextScaleTransform.Freeze();
        }

        /// <summary>Called to render a TreeMap</summary>
        /// <param name="dc"></param>
        protected override void OnRender(DrawingContext dc)
        {
            ++RenderCount;
            long startTick = Stopwatch.GetTimestamp();

            // get the tree of elements that will be rendered
            RGRTreeMapNode root = (RGRTreeMapNode)GetValue(TreeMapDataProperty);

            if (root == null)
            {
                base.OnRender(dc);
                // this is important: if there is no tree, then we render nothing
                // the tree map will be transparent.
                return;
            }

            // Reset the color index so we get the same color sequence each time.
#if DynamicBrushes
            rbrushesidx = 0;
#endif

            // save the DC so we don't have to pass it recrusivly on the stack
            theDc = dc;

            PaintedRectCount = 0;

            // Get the area limit - rectangles less than this in size
            // will not be rendered
            TheAreaLimit = (double)GetValue(AreaLimitProperty);

            ChildrenAreInset = InsetLength > 0.0;

            RGRTreeMapNode zr = ZoomRoot;
            if (zr != null)
            {
                root = zr;
            }

            RenderPreComputedTree(root);

            if (_SendRenderPerfEvent && RenderPerformanceValuesSignal != null)
            {
                this.Parent.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Input,
                                                   RenderPerformanceValuesSignal,
                                                   Stopwatch.GetTimestamp() - startTick, PaintedRectCount);
            }
        }
        /// <summary>
        /// Recursivly render the three
        /// </summary>
        /// <param name="node">the node to render</param>
        private void RenderPreComputedTree(RGRTreeMapNode node)
        {
            //Assert.IsFalse( node.BoundingRect.IsEmpty );
            //Assert.IsTrue( node.BoundingRect.Width  > 0.0 );
            //Assert.IsTrue( node.BoundingRect.Height > 0.0 );

            if (node.ChildCount == 0)
            {
                //
                // This node has no childeren so just paint it
                //
                RenderNodeRectangle(node);

            }
            else
            {

                if (ChildrenAreInset)
                {
                    RenderNodeRectangle(node);
                }

                if (node.NumToRender > 0)
                {
                    //
                    // This node has one or more children that need painting... 
                    // do so recursivly
                    //
                    for (int i = 0; i < node.NumToRender; ++i)
                    {

                        //Assert.IsTrue( node.myChildren[i].BoundingRect.Width  > 0.0 );
                        //Assert.IsTrue( node.myChildren[i].BoundingRect.Height > 0.0 );
                        //Assert.IsTrue( node.myChildren[i].BoundingRect.Height * node.myChildren[i].BoundingRect.Width > AreaLimit );

                        RenderPreComputedTree(node.myChildren[i]);
                    }
                }
                else
                {
                    // If there are no children to render, then the reminder rect
                    // is supposed to be empty
                    //Assert.IsTrue( node.RemainderBoundingRect.IsEmpty );
                }

                if (!node.RemainderBoundingRect.IsEmpty)
                {
                    //
                    // Paint the remainder rectangle if it has a non zero area
                    //
                    //(?) can node.NumToRender  be zero and the remainder rect have a non zero area?
                    //(A) No, if all of the children are too small to be displayed then
                    //    the remainder rectangle is will be empty.  See the //Assert in
                    //    the clause just above.
                    //Assert.IsTrue( node.RemainderBoundingRect.Width  > 0.0 );
                    //Assert.IsTrue( node.RemainderBoundingRect.Height > 0.0 );
                    //Assert.IsNotNull( node.RemainderBrush );
                    //Assert.IsNotNull( node.AreaBrush );

                    ++PaintedRectCount;
                    theDc.DrawRectangle(node.RemainderBrush, null, node.RemainderBoundingRect);
                }
            }
            //
            // Do this last so text and other adonrments are rendered with the items higher
            // in the tree rendering over (or on top of) items lower in the tree.  This is
            // in contrast to the calls to RenderNodeRectangle which ensure that 
            // the node's are rendered top to bottom, rendering the lower nodes over
            // (or on top of) the nodes higher in the tree.
            //
            RenderNodeTextAndAdornments(node);
        }

        /// <summary>
        /// Render the node's rectangle
        /// </summary>
        /// <param name="node"></param>
        private void RenderNodeRectangle(RGRTreeMapNode node)
        {
            //Assert.IsNotNull( node.AreaBrush );
            ++PaintedRectCount;
            theDc.DrawRectangle(node.AreaBrush, null, node.BoundingRect);
        }

        private void RenderNodeTextAndAdornments(RGRTreeMapNode node)
        {
            if (node.FText != null)
            {

#if false
                Rect tbr = new Rect( node.FTextLocation.X, node.FTextLocation.Y,
                                     node.FText.Width, node.FText.Height );
#endif

                //Assert.IsNotNull( node.FTextScaleTransform );
                theDc.PushOpacity(node.FTextOpacity);
                theDc.PushTransform(node.FTextScaleTransform);
#if false
                theDc.DrawRectangle( Brushes.Green, null, tbr );
#endif
                theDc.DrawText(node.FText, node.FTextLocation);
                theDc.Pop();
                theDc.Pop();
            }
        }

        /// <summary>Basic tree map layout algorithm
        /// </summary>
        /// <param name="node">the node on which to operate</param>
        /// <param name="lbr">the child rect used to size the children</param>
        /// <param name="count">
        /// The nmber of children to layout.  
        /// This function will process myChildren[ 0 ... count-1 ]
        /// </param>
        /// <param name="TotalSize">
        /// The total size of the children to layou.  This value must
        /// be the sume of the sizes for myChildren[ 0 ... count-1 ]
        /// </param>
        /// <remarks>
        /// Precondition: the node's rectangle must have a valid location and size
        /// Precondition: the node's children must be sorted by size
        ///               (largest first, smallest last)
        /// 
        /// This function sets the height and width of each child rectangle.  It stops
        /// when it finds a child with zero size or a child that has a visible area
        /// less than AeraLimit.
        /// 
        /// Any children that have too little visible area to be rendered
        /// have their bounding rectangles set to zero.
        /// 
        /// Note, this function doesn't need to handle verticale or horizontal layouts.
        /// Its onl function is to set the height and width of each child and thus
        /// each child's visible area. 
        /// 
        /// Note, this function is only responsible for setting the height and width
        /// of each rectangle.  Its not necessary to set X and Y here.
        /// </remarks>
        /// <returns>The number of children layed out.</returns>
        private int ComputeChildAreas(RGRTreeMapNode node, Rect cr, int count, double TotalSize)
        {
            double w;
            double h;
            double a;
            int i;

            h = cr.Height;

            for (i = 0; i < count; ++i)
            {

                if (node.myChildren[i].Size == 0.0)
                {
                    break;
                }

                w = cr.Width * (node.myChildren[i].Size / TotalSize);
                a = w * h;

                if (a < TheAreaLimit)
                {
                    break;
                }

#if DynamicBrushes
                //node.myChildren[i].AreaBrush = GetNextBrush();
#endif
                node.myChildren[i].BoundingRect.Width = w;
                node.myChildren[i].BoundingRect.Height = h;
                node.myChildren[i].Area = a;

                //Assert.IsNotNull( node.myChildren[i].AreaBrush );

                // note, no need to set X and Y of the child bounding
                // rect here.  This is set in LayoutRow().
            }

            if (i < count)
            {
                // all remaining children are too small to be rendered so
                // make them all zero.
                for (int j = i; j < count; j++)
                {
                    node.myChildren[i].BoundingRect.X = 0.0;
                    node.myChildren[i].BoundingRect.Y = 0.0;
                    node.myChildren[i].BoundingRect.Width = 0.0;
                    node.myChildren[i].BoundingRect.Height = 0.0;
                    node.myChildren[i].Area = 0.0;
#if DynamicBrushes
                    node.myChildren[i].AreaBrush = null;
#endif
                }
            }

            return i;
        }

        /// <summary>Lays out the children of a node
        /// </summary>
        /// <param name="node">
        /// Squareify and layout the children for this node.  The children
        /// layed out are children[0 .. (NumToRender-1)]
        /// </param>
        /// <param name="r">
        /// This is the enclosing rectangle for the children.  Its size
        /// and location must be set on entry to this function.
        /// <remarks>
        /// This function lays out the children of a node using the 
        /// Squareify algorithm.
        /// 
        /// This function has an important side effect - it sets the
        /// nodes remainder rectangle.  If the node's entire bounding
        /// rectangle was filled in with children, then the remainder
        /// rect will be empty, if not all of the children were 
        /// layed out then the remainder rect will be set to a none
        /// empty state in which case it should be painted. 
        /// </remarks>
        /// 
        private void SquareifyChildren(RGRTreeMapNode node, Rect r)
        {
            double worstAspectRatioSoFar = double.MaxValue;
            double nworst;
            int len;

            //Assert.IsTrue( TheKids.Capacity >= node.myChildren.Length );
            //Assert.IsTrue( row.Capacity     >= node.myChildren.Length );
            //Assert.IsTrue( row.Count     == 0 ); // the row list should always be empty here.
            //Assert.IsTrue( TheKids.Count == 0 ); // the kid list should always be empty here.

            // create a list of the children in a list: note we are using
            // a pre allocated list that is guarnteed to have the capacity
            // to hold the large number of children in the tree.  This prevents
            // allocation of the child list in the render path.
            //
            // Note that the kids list is backwards as we remove elements
            // from its tail below.
            //
            for (int i = node.NumToRender - 1; i >= 0; --i)
            {
                TheKids.Add(node.myChildren[i]);
            }

            //
            // Layout this row against the shortest side against which
            // the row's rectangles are placed.
            //
            double LayoutWidth = Math.Min(r.Width, r.Height);

            while ((len = TheKids.Count) > 0)
            {

                row.Add(TheKids[len - 1]);

                nworst = WorstAspectRatioOfRow(row, LayoutWidth);

                if (nworst < worstAspectRatioSoFar)
                {
                    //
                    // Accumulate nodes so long as we keep finding rectangles
                    // that improve the aspect ratio of the row
                    //
                    TheKids.RemoveAt(len - 1);
                    worstAspectRatioSoFar = nworst;

                }
                else
                {
                    //
                    // Ok, we have a list of children that need to be laid in
                    // a row in rectange 'r'
                    //
                    row.RemoveAt(row.Count - 1);               // remove the latest addition

                    LayoutRow(row, ref r, LayoutWidth);        // layout the row in Rect 'r' against length LayoutWidth

                    row.Clear();                                 // clear the row

                    LayoutWidth = Math.Min(r.Width, r.Height); // compute the new w

                    worstAspectRatioSoFar = Double.MaxValue;     // reset the worst aspect ratio
                }
            }

            if (row.Count > 0)
            {
                //
                // Layout any remaining children
                //
                LayoutRow(row, ref r, LayoutWidth);
                row.Clear();
            }

            //Assert.IsTrue( TheKids.Count == 0 );

            if (r.Width >= 1.0 && r.Height >= 1.0)
            {
                node.RemainderBoundingRect = r;
#if DynamicBrushes
                //node.RemainderBrush = GetNextBrush();
#endif
            }
            else
            {
                node.RemainderBoundingRect = Rect.Empty;
#if DynamicBrushes
                node.RemainderBrush = null;
#endif
            }
        }

        /// <summary>Layout a row of child nodes</summary>
        /// <param name="row">a list of children to layout in a row</param>
        /// <param name="r">
        /// The enclosing rectangle in which the rectangles are layed out.
        /// This is a referenced parameter and is adjusted to be the 
        /// remaing reion for further layout.
        /// </param>
        /// <param name="LayoutWidth">the width against which the children are layed out</param>
        private void LayoutRow(List<RGRTreeMapNode> row, ref Rect r, double LayoutWidth)
        {
            double rowTotalArea = 0; // sum areas of rectangles in this row

            for (int i = 0; i < row.Count; ++i)
            {
                rowTotalArea += row[i].Area;
            }

            double new_height = rowTotalArea / LayoutWidth;

            bool layoutHorizontaly = (LayoutWidth == r.Width);

            double x = r.X;     // starting horizontal position
            double y = r.Y;     // starting vertical postion

            double d = 0.0;     // position delta
            double new_width;   // new width for a rectangle

            for (int i = 0; i < row.Count; ++i)
            {

                new_width = row[i].Area / new_height;

                if (layoutHorizontaly)
                {
                    row[i].BoundingRect.X = x + d;
                    row[i].BoundingRect.Y = y;
                    row[i].BoundingRect.Width = new_width;
                    row[i].BoundingRect.Height = new_height;
                }
                else
                {
                    row[i].BoundingRect.X = x;
                    row[i].BoundingRect.Y = y + d;
                    row[i].BoundingRect.Width = new_height;
                    row[i].BoundingRect.Height = new_width;
                }

                d += new_width;
            }

            //
            // update space available in rectangle r
            //
            if (layoutHorizontaly)
            {
                // r.X       doesn't change
                r.Y = y + new_height;
                // r.Width   doesn't change
                r.Height = floorz(r.Height - new_height);
            }
            else
            {
                r.X = x + new_height;
                // r.Y       doesn't change
                r.Width = floorz(r.Width - new_height);
                // r.Height  dosn't change
            }
        }

        /// <summary>If v is less than zero, then return zero.
        /// </summary>
        private double floorz(double v)
        {
            if (v < 0)
            {
                return 0.0;
            }
            else
            {
                return v;
            }
        }

        /// <summary>Compute a rows worst aspec ratio.
        /// </summary>
        /// <param name="c">a list of nodes</param>
        /// <param name="LayoutWidth">
        /// the length of the side of a rectangle against which the
        /// children are to be layed out.
        /// </param>
        /// <remarks>
        /// Return the worst aspect ratio of the nodes in list 'c' as compared
        /// to the side of a rectangle (either its width or height) as specfied
        /// in paremter 'LayoutWidth'
        /// </remarks>
        private double WorstAspectRatioOfRow(List<RGRTreeMapNode> c, double L)
        {
            double maxArea = Double.MinValue;
            double minArea = Double.MaxValue;
            double totalArea = 0.0;
            double area;

            //
            // compute the maximum size, the minimum size, and the total
            // size for all the nodes in 'c'
            //
            for (int i = 0; i < c.Count; ++i)
            {
                area = c[i].Area;
                //Assert.AreNotEqual( area, double.NaN );
                //Assert.AreNotEqual( area, 0.0 );
                totalArea += area;
                maxArea = Math.Max(maxArea, area);
                minArea = Math.Min(minArea, area);
            }

            totalArea = totalArea * totalArea;
            L = L * L;

            double a1 = L * (maxArea / totalArea);   // aspect ratio of largest row item
            double a2 = totalArea / (L * minArea);   // aspect ratio of smallest row item

            return Math.Max(a1, a2);
        }

        /// <summary>Computes the aspect ratio (width/height) of a rectangle.
        /// </summary>
        private static double RAspectRatio(Rect r)
        {
            return r.Width / r.Height;
        }

#if DynamicBrushes
#else
        private void SetAllNodeBrushes( RGRTreeMapNode node )
        {
            rbrushesidx = 0;
            SetAllNodeBrushesR( node );
        }

        private void SetAllNodeBrushesR( RGRTreeMapNode node )
        {
            node.AreaBrush      = GetNextBrush();
            node.RemainderBrush = GetNextBrush();

            for ( int i = 0; i < node.ChildCount; i++ ) {
                SetAllNodeBrushesR( node.Children[i] );
            }
        }
#endif

        private Brush GetNextBrush()
        {
            Brush b = rbrushes[rbrushesidx];

            if ((++rbrushesidx) == rbrushes.Length)
            {
                rbrushesidx = 0;
            }

            return b;
        }

       

        /// <summary>
        /// Zoom in and out based on the mouse buttons
        /// </summary>
        protected virtual void MouseDownHandler(object sender, MouseButtonEventArgs e)
        {
            RGRTreeMapNode rendered_root = TreeMapData;

            if (rendered_root == null)
            {
                return;
            }

            RGRTreeMapNode zr = ZoomRoot;

            if (zr != null)
            {
                rendered_root = zr;
            }

            RGRTreeMapNode tmn = rendered_root.FindNodeByPoint(e.GetPosition(this));

            if (tmn != null)
            {
                if (e.LeftButton == MouseButtonState.Pressed)
                {

                    if (tmn != zr)
                    {
                        if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
                        {
                            // Zoom into parent of selected node
                            ZoomRoot = tmn.Parent;
                        }
                        else
                        {
                            // Zoom in one level...
                            while (tmn.Parent != rendered_root)
                            {
                                tmn = tmn.Parent;
                            }
                            ZoomRoot = tmn;
                        }
                    }
                }
                else
                {
                    if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
                    {
                        // un-zoom
                        ZoomRoot = null;
                    }
                    else
                    {
                        // pop up one level if no at true node
                        if (zr != null)
                        {
                            ZoomRoot = zr.Parent;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Handles mouse overs
        /// </summary>
        /// <remarks>
        /// If this controls MouseOverSignal delegate is set, then this 
        /// function will invoke that delegate asyncronously to inform the
        /// target which tree map node the mouse is over.   
        /// </remarks>
        private void HandleMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
        {

           // this.ToolTip = "testr";
            
            if (MouseOverSignal == null)
            {
                return;
            }

            RGRTreeMapNode root = TreeMapData;

            if (root == null)
            {
                return;
            }

            RGRTreeMapNode zr = ZoomRoot;

            if (zr != null)
            {
                root = zr;
            }

            Point MousePos = e.GetPosition(this);

            long startTick = Stopwatch.GetTimestamp();

            RGRTreeMapNode tmn = root.FindNodeByPoint(MousePos);

            if (tmn == null)
            {
                return;
            }


            bool MouseInRemainderRect = tmn.RemainderBoundingRectCointains(MousePos);

            if (!Object.ReferenceEquals(MostRecentMouseNode, tmn))
            {
                //
                // The mouse over node has changed.  Step 1: restore
                // the burshes to the last mouse over node and its parent
                //
                RestoreMouseOverBrushes();

                //
                // Step 2: Save the new mouse over node's reference, its brush
                // and its parent's brush (if appropriate)
                //
                MostRecentMouseNode = tmn;

                //
                // Step 3: set the node brushes to the approprite highlight colors
                //
                if (MouseInRemainderRect)
                {
                    //
                    // The mouse is in a node's remainder rectangle.  So highlight 
                    // the remandiner rectagnel with the mouse over highlgiht brush
                    // and the node it self with the highlight border brush
                    //
                    MostRecentMouseNodeInRemainderRect = true;

                    MostRecentMouseOverHighlightBrush = tmn.RemainderBrush;
                    tmn.RemainderBrush = Brushes.Red;

                    MostRecentMouseOverBorderHighlightBrush = tmn.AreaBrush;
                    tmn.AreaBrush = Brushes.Black;

                }
                else
                {

                    MostRecentMouseNodeInRemainderRect = false;

                    MostRecentMouseOverHighlightBrush = tmn.AreaBrush;
                    tmn.AreaBrush = Brushes.Red;

                    if (tmn.Parent != null)
                    {
                        MostRecentMouseOverBorderHighlightBrush = tmn.Parent.AreaBrush;
                        tmn.Parent.AreaBrush = Brushes.Black;
                    }
                }

                this.InvalidateVisual();

                tmn._MouseOverTicks = Stopwatch.GetTimestamp() - startTick;

                if (_MouseOverIsSyncronous)
                {
                    MouseOverSignal(tmn, MouseInRemainderRect, MousePos);
                }
                else
                {
                    Parent.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Input,
                                                   MouseOverSignal, tmn, MouseInRemainderRect, MousePos);
                }
            }
        }

        /// <summary>
        /// Restore the brushes set by a mouse over
        /// </summary>
        private void RestoreMouseOverBrushes()
        {
            if (MostRecentMouseNode != null)
            {
                if (MostRecentMouseNodeInRemainderRect)
                {
                    MostRecentMouseNode.RemainderBrush = MostRecentMouseOverHighlightBrush;
                    MostRecentMouseNode.AreaBrush = MostRecentMouseOverBorderHighlightBrush;
                }
                else
                {
                    MostRecentMouseNode.AreaBrush = MostRecentMouseOverHighlightBrush;
                    if (MostRecentMouseNode.Parent != null)
                    {
                        MostRecentMouseNode.Parent.AreaBrush = MostRecentMouseOverBorderHighlightBrush;
                    }
                }
            }
        }

        /// <summary>
        /// Reset the state of the mouse over members when the mouse leaves the control
        /// </summary>
        /// <remarks>
        /// This function will never call the MouseOverSignal delegate if there is no
        /// tree map data (TreeMapData == null).
        /// </remarks>
        private void MouseLeaveHandler(object sender, System.Windows.Input.MouseEventArgs e)
        {
            if (MouseOverSignal == null || TreeMapData == null)
            {
                return;
            }

            RestoreMouseOverBrushes();
            MostRecentMouseNode = null;
            MostRecentMouseNodeInRemainderRect = false;
            this.InvalidateVisual();

            if (_MouseOverIsSyncronous)
            {
                MouseOverSignal(null, false, new Point(0, 0));
            }
            else
            {
                Parent.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Input,
                                               MouseOverSignal, null, false, new Point(0, 0));
            }
        }

        /// <summary>
        /// Enables the render performance event when true
        /// </summary>
        public bool SendRenderPerfEvent
        {
            get { return _SendRenderPerfEvent; }
            set { _SendRenderPerfEvent = value; }
        }
        private bool _SendRenderPerfEvent = false;

        public bool MouseOverIsSyncronous
        {
            get { return _MouseOverIsSyncronous; }
            set { _MouseOverIsSyncronous = value; }
        }
        private bool _MouseOverIsSyncronous = false;

        /// <summary>Computes the path for a node</summary>
        /// <param name="node">the node for which to compute the path</param>
        /// <param name="separator">string that separtes names in the path.  may be null</param>
        /// <returns>
        /// A string representing the path from the root of the tree to 
        /// (and including) the node.
        /// </returns>
        /// <remarks>
        /// This function computes a string that represents the path to the 
        /// node.  The path starts with the name of the tree's root node
        /// and includes the name of each node between, and including
        /// the specified node. If separator is not null, then
        /// this string will be placed between each name in the returned
        /// string.  The separater is not appended to the end of the
        /// string (after the name of the target node).
        /// </remarks>
        public String ComputeNodePath(RGRTreeMapNode node, String separator)
        {
            sb.Length = 0;

            //Assert.IsTrue( row.Count == 0 );

            RGRTreeMapNode tmp = node;
            int nc = -1;

            do
            {
                ++nc;
                row.Add(tmp);
                tmp = tmp.Parent;
            } while (tmp != null);

            for (int i = nc; nc >= 0; --nc)
            {
                sb.Append(row[nc].Name);
                if (separator != null && nc > 0)
                {
                    sb.Append(separator);
                }
            }

            row.Clear();

            return sb.ToString();
        }

        //------------------------------------------------------------------------------------------------------------------------
        // Dependancy Properties
        //------------------------------------------------------------------------------------------------------------------------

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
        // TreeMapData dependancy property

        public static readonly DependencyProperty TreeMapDataProperty = DependencyProperty.Register(
            "TreeMapData",                      // The name of the dependency property to register
            typeof(RGRTreeMapNode),           // The type of the property
            typeof(RGRTreeMapClass),          // The owner type that is registering the dependency property.
            new FrameworkPropertyMetadata(      // Property metadata for the dependency property
                null,
                FrameworkPropertyMetadataOptions.AffectsRender,
                new PropertyChangedCallback(OnTreeMapDataChanged)
                )
            );

        public RGRTreeMapNode TreeMapData
        {
            get { return (RGRTreeMapNode)GetValue(TreeMapDataProperty); }
            set { SetValue(TreeMapDataProperty, value); }
        }

        private static void OnTreeMapDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RGRTreeMapNode newroot = e.NewValue as RGRTreeMapNode;
            RGRTreeMapClass theTreeMap = d as RGRTreeMapClass;

            theTreeMap.ZoomRoot = null;

            theTreeMap.MaxChildren = newroot.ComputeMaxChildCount();
            theTreeMap.row = new List<RGRTreeMapNode>(theTreeMap.MaxChildren);
            theTreeMap.TheKids = new List<RGRTreeMapNode>(theTreeMap.MaxChildren);

#if DynamicBrushes
#else
            theTreeMap.SetAllNodeBrushes( newroot );
#endif
            theTreeMap.ComputeAllNodeBoundingRects(newroot);
        }

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
        // ZoomRoot dependancy property

        public static readonly DependencyProperty ZoomRootProperty = DependencyProperty.Register(
            "ZoomRoot",                         // The name of the dependency property to register
            typeof(RGRTreeMapNode),           // The type of the property
            typeof(RGRTreeMapClass),          // The owner type that is registering the dependency property.
            new FrameworkPropertyMetadata(      // Property metadata for the dependency property
                null,
                FrameworkPropertyMetadataOptions.AffectsRender,
                new PropertyChangedCallback(OnZoomRootChanged)
                )
            );

        public RGRTreeMapNode ZoomRoot
        {
            get { return (RGRTreeMapNode)GetValue(ZoomRootProperty); }
            set { SetValue(ZoomRootProperty, value); }
        }

        private static void OnZoomRootChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RGRTreeMapNode zoomroot = e.NewValue as RGRTreeMapNode;
            RGRTreeMapClass theTreeMap = d as RGRTreeMapClass;

            if (zoomroot == null)
            {
                // Setting the ZoomRoot to null sets the default computation and render
                // point to the true node of the data tree
                zoomroot = theTreeMap.TreeMapData;
            }

            theTreeMap.ComputeAllNodeBoundingRects(zoomroot);
        }

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
        // AreaLimit dependancy property

        public static readonly DependencyProperty AreaLimitProperty = DependencyProperty.Register(
            "AreaLimit",                        // The name of the dependency property to register
            typeof(double),                   // The type of the property
            typeof(RGRTreeMapClass),          // The owner type that is registering the dependency property.
            new FrameworkPropertyMetadata(      // Property metadata for the dependency property
                64.0,
                FrameworkPropertyMetadataOptions.AffectsRender,
                new PropertyChangedCallback(OnAreaLimitChanged)
                )
            );

        public double AreaLimit
        {
            get { return (double)GetValue(AreaLimitProperty); }
            set { SetValue(AreaLimitProperty, value); }
        }

        private static void OnAreaLimitChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RGRTreeMapClass theTreeMap = d as RGRTreeMapClass;
            RGRTreeMapNode root = theTreeMap.TreeMapData;
            if (root != null)
            {
                theTreeMap.ComputeAllNodeBoundingRects(theTreeMap.TreeMapData);
            }
        }

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
        // InsetLength dependancy property

        public static readonly DependencyProperty InsetLengthProperty = DependencyProperty.Register(
            "InsetLength",                      // The name of the dependency property to register
            typeof(double),                   // The type of the property
            typeof(RGRTreeMapClass),          // The owner type that is registering the dependency property.
            new FrameworkPropertyMetadata(      // Property metadata for the dependency property
                2.5,
                FrameworkPropertyMetadataOptions.AffectsRender,
                new PropertyChangedCallback(OnInsetLengthChanged)
                )
            );

        public double InsetLength
        {
            get { return (double)GetValue(InsetLengthProperty); }
            set { SetValue(InsetLengthProperty, value); }
        }

        private static void OnInsetLengthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RGRTreeMapClass theTreeMap = d as RGRTreeMapClass;
            RGRTreeMapNode root = theTreeMap.TreeMapData;
            if (root != null)
            {
                theTreeMap.ComputeAllNodeBoundingRects(theTreeMap.TreeMapData);
            }
        }

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

        public delegate void MouseOverDelegate(RGRTreeMapNode node, bool MouseInRemainderRect, Point MousePos);
        public MouseOverDelegate MouseOverSignal;

        public delegate void RenderPerformanceValuesDelegate(long RunTime, long PaintedRects);
        public RenderPerformanceValuesDelegate RenderPerformanceValuesSignal;

        public delegate void ComputeAllNodeBoundingRectsPerformanceDelegate(long RunTime);
        public ComputeAllNodeBoundingRectsPerformanceDelegate ComputeAllNodeBoundingRectsPerformanceSignal;

        private int rbrushesidx;
        private SolidColorBrush[] rbrushes = new SolidColorBrush[] {
                Brushes.Olive,         Brushes.MediumSeaGreen, Brushes.Khaki,          Brushes.Turquoise, Brushes.Silver,
                Brushes.LightBlue,     Brushes.Pink,           Brushes.Tomato,         Brushes.LightBlue, Brushes.LightCoral,
                Brushes.YellowGreen,   Brushes.Salmon,         Brushes.RosyBrown
                };

        private int MaxChildren;
        private List<RGRTreeMapNode> row;
        private List<RGRTreeMapNode> TheKids;
        private DrawingContext theDc;
        private RGRTreeMapNode MostRecentMouseNode;
        private Brush MostRecentMouseOverHighlightBrush;
        private Brush MostRecentMouseOverBorderHighlightBrush;
        private bool MostRecentMouseNodeInRemainderRect;
        private double TheAreaLimit;
        private double TheInsetLength;
        private bool ChildrenAreInset;

        public Typeface TextTypeFace;
        public double TextSize;
        public Brush TextBrush;

        private StringBuilder sb = new StringBuilder(1024);

        #region Performance Counters

        /// <summary>Counts the number of times this control has been rendered</summary>
        public int RenderCount;

        /// <summary>The number of times the bounding rectangles have been computed</summary>
        public int ComputeBoundingRectsCount;

        /// <summary>The number of rectangles that are actually rendered</summary>
        public int PaintedRectCount;

        ///<summary>The number of Visible Nodes</summary>
        public int VisibleNodeCount;

        ///<summary>The number of Visible Rectangles</summary>
        public int VisibleRectCount;

        ///<summary>The number of nodes computed in the last computation</summary>
        public int VisibleRemainderRectCount;

        #endregion
    }
}